洛谷 P1174 打砖块

题目描述

小红很喜欢玩一个叫打砖块的游戏,这个游戏的规则如下:

在刚开始的时候,有n行*m列的砖块,小红有k发子弹。小红每次可以用一发子弹,打碎某一列当前处于这一列最下面的那块砖,并且得到相应的得分。(如图所示)

某些砖块在打碎以后,还可能将得到一发子弹的奖励。最后当所有的砖块都打碎了,或者小红没有子弹了,游戏结束。

小红在游戏开始之前,就已经知道每一块砖在打碎以后的得分,并且知道能不能得到一发奖励的子弹。小红想知道在这次游戏中她可能的最大得分,可是这个问题对于她来说太难了,你能帮帮她吗?

输入输出格式

输入格式:

第一行有3个正整数,n,m,k。表示开始的时候,有n行*m列的砖块,小红有k发子弹。

接下来有n行,每行的格式如下:

f1 c1 f2 c2 f3 c3 …… fm cm

其中fi为正整数,表示这一行的第i列的砖,在打碎以后的得分。ci为一个字符,只有两种可能,Y或者N。Y表示有一发奖励的子弹,N表示没有。

所有的数与字符之间用一个空格隔开,行末没有多余的空格。

输出格式:

仅一个正整数,表示最大的得分。

输入输出样例

输入样例#1:

3 4 2
9 N 5 N 1 N 8 N
5 N 5 Y 5 N 5 N
6 N 2 N 4 N 3 N

输出样例#1:

13

说明

对于20%的数据,满足1<=n,m<=5,1<=k<=10,所有的字符c都为N

对于50%的数据,满足1<=n,m<=200,1<=k<=200,所有的字符c都为N

对于100%的数据,满足1<=n,m<=200,1<=k<=200,字符c可能为Y

对于100%的数据,所有的f值满足1<=f<=10000

题解

感觉自己好菜,只会写没有奖励的50分,如果没有奖励这个题就是一个分组dp。由于打每一列的砖块,对其列是没有影响的,故没有后效性,所以我们可以直接对列进行枚举。
下面我们看看此题的正解,可以看到如果存在一个有奖励的砖块,你是首先要花费一颗子弹打,才能得到一颗子弹的奖励,故我们可以分两种情况讨论:

1.s1[i][j] 表示打第i行用了j颗子弹,且最后一颗子弹不打(留着给下一列进行开始)。
2.s2[i][j] 表示打第i行用了j颗子弹,且最后一颗子弹打掉。

第一个事情就是这两个数组明显可以预处理。第二个事情就是我们如何将它们运用到dp中,对应的我们给出dp数组的定义:

1.dp1[i][j] 表示打到第i行用了j颗子弹,且最后一颗子弹不打的最高得分。
2.dp2[i][j] 表示打到第i行用了j颗子弹,且最后一颗子弹打掉的最高得分。

最后一个问题就是方程的转移:

1.dp1[i][j]=max(dp1[i][j],dp1[i-1][j-g])
用g颗子弹打,由于上一列最后一个本身没有打和这一行的相对应,所以使用子弹数量不变,直接转移。

2.dp2[i][j]=max(dp2[i][j],dp1[i-1][j-g]+s2[i][g])
用g颗子弹打,由于上一列最后一个本身没有打,而这一列的最后一个需要打掉,相当于先最后打掉了第g个砖块。

2.dp2[i][j]=max(dp2[i][j],dp2[i-1][j-g]+s1[i][g])
用g颗子弹打,由于上一列最后一个本身打掉了,而你现在用j颗子弹只能打到第g-1个砖块。

可以看到,每次我们进行了对第i列所用子弹进行了枚举,那么如果上一行的最后一颗子弹打了,就要用”额外”的一颗子弹开始打,这个可以想一下。对应上上面的三种情况。

以上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
int a[210][210],s1[210][210],s2[210][210],dp1[210][210],dp2[210][210],n,m,k;
bool ward[210][210];
int main()
{
#ifdef YSW
freopen("in.txt","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
char ch[11];
scanf("%d%s",&a[i][j],ch);
if(ch[0]=='Y')ward[i][j]=true;
}
for(int i=1;i<=m;i++){
int cnt=0;
for(int j=n;j>=1;j--)
if(ward[j][i])
s1[i][cnt]+=a[j][i];
else
s1[i][++cnt]=s2[i][cnt]=s1[i][cnt-1]+a[j][i];
}
for(int i=1;i<=m;i++)
for(int j=0;j<=k;j++)
for(int g=0;g<=n&&g<=j;g++){
dp1[i][j]=max(dp1[i][j],dp1[i-1][j-g]+s1[i][g]);
if(g>0)
dp2[i][j]=max(dp2[i][j],dp1[i-1][j-g]+s2[i][g]);
if(j-g>0)
dp2[i][j]=max(dp2[i][j],dp2[i-1][j-g]+s1[i][g]);
}
printf("%d\n",dp2[m][k]);
return 0;
}

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 题目描述
  2. 2. 输入输出格式
    1. 2.1. 输入格式:
    2. 2.2. 输出格式:
      1. 2.2.1. 输入样例#1:
      2. 2.2.2. 输出样例#1:
      3. 2.2.3. 说明
  3. 3. 题解
,